/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server;


import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ArrayList;
import datagram.*;
import java.io.IOException;
import java.net.DatagramPacket;
import java.util.Iterator;


/**
 *
 * @author rmb
 */
public class DataSend extends Thread {
    
    private DatagramSocket socket;
    private InetAddress address;
    private int port;
    private ArrayList<Integer> expected_acks;
    private ArrayList<Integer> confirmed_acks;
    private boolean pause;
    private boolean stop;
   

    public DataSend(DatagramSocket socket, InetAddress address, int port) {
        this.socket = socket;
        this.address = address;
        this.port = port;
        this.expected_acks = new ArrayList<Integer>();
        this.confirmed_acks = new ArrayList<Integer>();
        this.pause = false;
        this.stop = false;
    }
    
    public boolean getStop(){
        return stop;
    }
    
    public void sendCONNECT() {
        
        Datagram d = new Datagram(Datagram.UDPDatagram.CONNECT, -1, null);
        byte[] send = Datagram.toByteArray(d);
        DatagramPacket pack = new DatagramPacket(send, send.length, address, port);
        
        try {
            socket.send(pack);
        }
        catch(IOException e) {}
    }
    
    
    public void sendDISCONECT() {
        
        Datagram d = new Datagram(Datagram.UDPDatagram.DISCONNECT, -1, null);
        byte[] send = Datagram.toByteArray(d);
        DatagramPacket pack = new DatagramPacket(send, send.length, address, port);
        
        try {
            socket.send(pack);
        }
        catch(IOException e) {}
    }
    
    
    public void sendACK(int pack) {
        boolean found = false;        
        Iterator<Integer> it = expected_acks.iterator();
        int elem;
        
        while(it.hasNext() && !found) {
            
            elem = it.next();
            if(elem == pack) {
                found = true;
                
                Datagram d = new Datagram(Datagram.UDPDatagram.ACK, pack, null);
                byte[] send = Datagram.toByteArray(d);
                DatagramPacket dp = new DatagramPacket(send, send.length, address, port);
                
                try {
                    socket.send(dp);
                }
                catch(Exception e){}
                
                it.remove();
                confirmed_acks.add(elem);                
            }            
        }                
    }
    
    
    @Override
    public void run () {
        try {
            sendCONFIRMATIONS();
            sendDISCONECT();          
        }
        catch(Exception e) {
            System.out.println("Unknown packet");
        }
        
    }
    
    protected synchronized void sendCONFIRMATIONS() {
        
        while(!stop) {
            
            while(expected_acks.isEmpty() && !stop)
                pause();
               
            if(!stop) {
                Datagram d = new Datagram(Datagram.UDPDatagram.ACK, expected_acks.get(0), null);
                byte[] buffer = Datagram.toByteArray(d);
                DatagramPacket dp = new DatagramPacket(buffer, buffer.length, address, port);
                
                try{
                    socket.send(dp);
                }
                catch(IOException e) {}
                
                confirmed_acks.add(expected_acks.get(0));
                expected_acks.remove(0);                
            }                        
        }        
    }
    
    public synchronized void pause() {
            pause = true;            
            try {
                while (pause)
                    wait();
            }
            catch(InterruptedException e) {}
    }

    public synchronized void unpause(){
        pause = false;
        notifyAll();
    }
    
    public synchronized void putExpectedPack (int num) {
        expected_acks.add(num);                
        this.unpause();
    }
    
    public synchronized void setStop() {
        stop = true;
        this.unpause();
        this.sendDISCONECT();
    }

    public ArrayList<Integer> getConfirmed_acks() {
        return confirmed_acks;
    }

    public ArrayList<Integer> getExpected_acks() {
        return expected_acks;
    }
    
    public synchronized void incExpectedNum(int num){
        expected_acks.add(num);
        unpause();
    }
    
    
    
}
